float4x4 View;
float4x4 Projection;

float3 CameraPosition;
float3 FrustumCorners[4];

float2 NoiseScale;

#define KernelSize 10
float3 Kernel[KernelSize];

#define Radius 4

texture RandomTexture;
sampler randomSampler = sampler_state
{
    Texture = <RandomTexture>;
    AddressU = Wrap;
    AddressV = Wrap;
    MagFilter = Point;
    MinFilter = Point;
    Mipfilter = Point;
};

texture Normal;
sampler normalSampler = sampler_state
{
    Texture = <Normal>;
    AddressU = Clamp;
    AddressV = Clamp;
    MagFilter = Point;
    MinFilter = Point;
    Mipfilter = Point;
};

texture Depth;
sampler depthSampler = sampler_state
{
    Texture = <Depth>;
    AddressU = Clamp;
    AddressV = Clamp;
    MagFilter = Point;
    MinFilter = Point;
    Mipfilter = Point;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position   : POSITION0;
    float2 TexCoord   : TEXCOORD0;
    float3 FrustumRay : TEXCOORD1;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    output.Position = input.Position;
    output.TexCoord = input.TexCoord;
    output.FrustumRay = FrustumCorners[input.TexCoord.x + input.TexCoord.y * 2];

    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float depth = tex2D(depthSampler, input.TexCoord);
    float3 origin = (input.FrustumRay - CameraPosition) * depth + CameraPosition;
    float3 normal = tex2D(normalSampler, input.TexCoord).xyz * 2 - 1;

    float3 rvec = tex2D(randomSampler, input.TexCoord * NoiseScale);
    rvec.z = 0;

    float3 tangent = normalize(rvec - normal * dot(rvec, normal));
    float3 bitangent = cross(normal, tangent);

    float3x3 tbn = float3x3(tangent, bitangent, normal);

    float occlusion = 0.0;
    for (int i = 0; i < KernelSize; i++)
    {
        // Get sample position
        float3 sample = mul(Kernel[i], tbn);//mul(tbn, Kernel[i]);
        sample = sample * Radius + origin;

        // Project sample position
        float4 offset = float4(sample, 1.0);
        offset = mul(mul(offset, View), Projection);
        offset.xy /= offset.w;
        offset.y = -offset.y;
        offset.xy = offset.xy * 0.5f + 0.5f;

        // Get sample depth
        float sample_depth = tex2D(depthSampler, offset.xy);

        // Range check and accumulate
        float range_check = abs(depth - sample_depth) < Radius ? 1.0 : 0.0;
        occlusion += (sample_depth <= depth? 1.0 : 0.0) * range_check;
    }

    occlusion /= KernelSize;
    return 1 - occlusion;
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }
}
